
#include <stdio.h>
#include <string.h>
#include <map>
#include <string>
#include <Windows.h>
using namespace std;

#define DEF_ARG(a, b, c) const char* arg_##a[] = {#b, #c};
DEF_ARG(time, time, t);
DEF_ARG(set, set, s);
DEF_ARG(debug, debug, d);
DEF_ARG(away, away, a);
DEF_ARG(display, display, dis);
DEF_ARG(system, system, sys);
#undef DEF_ARG
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))

struct arg_t
{
public:
	arg_t(const int argc, const char* argv[]) { init(argc, argv); }
#define DEF_ARG(a) bool a() const { return _##a; }
	DEF_ARG(has_time);
	DEF_ARG(away);
	DEF_ARG(display);
	DEF_ARG(set);
	DEF_ARG(system);
	DEF_ARG(debug);
#undef DEF_ARG
	int time() const { return _time; }
private:
#define IS_ARG(a) \
	bool is_##a(const string& key) \
	{ \
		for(int i = 0; i < ARRAY_SIZE(arg_##a); i++) \
		{ \
			if(key == arg_##a[i]) return true; \
		} \
		return false; \
	}
	IS_ARG(time);
	IS_ARG(set);
	IS_ARG(debug);
	IS_ARG(away);
	IS_ARG(display);
	IS_ARG(system);
#undef IS_ARG

	void init(const int argc, const char* argv[])
	{
		_has_time = false;
		_time = 0;
		_set = false;
		_away = false;
		_display = false;
		_system = false;
		_debug = false;

		string last;
		// the first arg is application name
		for(int i = 1; i < argc; i++)
		{
			if(strlen(argv[i]) > 0)
			{
				if(argv[i][0] == '-' || argv[i][0] == '/')
				{
					last = string(&argv[i][1]);
					arg.insert(make_pair(last, string()));
				}
				else
				{
					arg[last] = string(argv[i]);
					last.clear();
				}
			}
		}

		for(auto it = arg.begin(); it != arg.end(); it++)
		{
			const string& key = (*it).first;
			const string& value = (*it).second;

#define bool_set(a) (is_##a(key)) _##a = true
			if bool_set(set);
			else if bool_set(debug);
			else if bool_set(away);
			else if bool_set(display);
			else if bool_set(system);
#undef bool_set
			else if(is_time(key))
			{
				int v = atoi(value.c_str());
				if(v > 0)
				{
					_has_time = true;
					_time = v;
				}
			}
		}

		debug_print_arg();
	}

	void debug_print_arg()
	{
#ifndef DEBUG
		if(debug())
#endif
		{
			for(auto it = arg.begin(); it != arg.end(); it++)
				printf("%s => %s\n", (*it).first.c_str(), (*it).second.c_str());
			if(has_time()) printf("time %d\n", time());
			if(display()) printf("display\n");
			if(system()) printf("system\n");
			if(away()) printf("away\n");
			if(set()) printf("set\n");
			if(debug()) printf("debug\n");
		}
	}

	map<string, string> arg;
	bool _has_time;
	int _time;
	bool _away;
	bool _display;
	bool _set;
	bool _system;
	bool _debug;
};

void set_state(const DWORD& r) { if(SetThreadExecutionState(r) == NULL) printf("SetThreadExecutionState failed\n"); }

#define debug_print(x) if(arg.debug()) printf(#x"\n");
void set_state(const arg_t& arg)
{
	debug_print(set thread execution state);
	DWORD r = ES_CONTINUOUS;
	if(arg.system())
	{
		debug_print(system mode enabled);
		r |= ES_SYSTEM_REQUIRED;
	}
	if(arg.away())
	{
		debug_print(away mode enabled);
		r |= ES_AWAYMODE_REQUIRED;
	}
	if(arg.display())
	{
		debug_print(display mode enabled);
		r |= ES_DISPLAY_REQUIRED;
	}
	set_state(r);
}

void reset_state(const arg_t& arg)
{
	debug_print(reset thread execution state);
	set_state(ES_CONTINUOUS);
}
#undef debug_print

#define DO(a, b) \
	bool a(const arg_t& arg) \
	{ \
		if(arg.##a()) \
		{ \
			b; \
			return true; \
		} \
		return false; \
	}
	DO(set, set_state(arg));
	DO(has_time, Sleep(arg.time() * 1000));
#undef DO

void print_help()
{
	printf("supported commands\n"
		   "-%s -- set the thread execution state.\n"
		   "-%s -- sleep for a certain time, say -%s 1 means sleep for 1 second.\n"
		   "-%s -- use system mode.\n"
		   "-%s -- use away mode.\n"
		   "-%s -- use display mode.\n"
		   "briefly, "
		   "system mode will prevent to enter standby mode, "
		   "display mode will also prevent to shutdown the display, "
		   "away mode will prevent from S3 sleep, "
		   "if none of three modes are selected, this application will only reset the idel timer.\n"
		   "if none of %s and %s are selected, this application will print this help.\n"
		   "a typical usage is -%s -%s -%s 100, to prevent computer from entering standby mode for 100 seconds.\n"
		   "or -%s 100, to work just as sleep 100s in mingw.\n"
		   "for detail about the mode, please refer to http://msdn.microsoft.com/en-us/library/aa373208(VS.85).aspx",
		   arg_set[0],
		   arg_time[0], arg_time[0],
		   arg_system[0],
		   arg_away[0],
		   arg_display[0],
		   arg_set[0], arg_time[0],
		   arg_set[0], arg_system[0], arg_time[0],
		   arg_time[0]);
}

int main(const int argc, const char* argv[])
{
	arg_t arg(argc, argv);
	bool r = set(arg);
	if(!has_time(arg) && !r) print_help();
	else if(r) reset_state(arg);
}
